Skip to content

Conversation

@pitzcarraldo
Copy link
Contributor

@pitzcarraldo pitzcarraldo commented Jan 21, 2026

Summary

Add comprehensive Firebase configuration detection and validation system. Users can now automatically detect Firebase credential files (google-services.json for Android, GoogleService-Info.plist for iOS), validate them against Firebase schemas, and optionally download configs directly from Firebase Console using OAuth 2.0 authentication.

Details

Core Components:

  • detector.ts: Automatically detects Firebase credential files and their locations
  • validator.ts: Validates credentials against Zod schemas with detailed error messages
  • firebase-service.ts: Main service orchestrating detection, validation, and recommendations
  • FirebaseWizard.tsx: Interactive UI for configuration management
  • OAuth 2.0 authentication with PKCE support for Firebase Management API access

Key Features:

  • Supports React Native, Flutter, iOS, and Android platforms
  • Handles both JSON and XML plist formats for iOS configuration
  • Cross-platform project ID verification
  • Detailed issue reporting with recommendations and help URLs
  • /firebase command for interactive configuration workflow

How to Validate

  1. Run bun run dev to start interactive mode
  2. Type /firebase to open the Firebase configuration wizard
  3. Test detection with sample Firebase config files
  4. Verify output for both valid and invalid configurations

Pre-Merge Checklist

Code Quality

  • Code builds without errors (bun run build)
  • Types check correctly (bun run typecheck)
  • Linter passes (bun run lint)
  • Tests pass (bun test - 529 tests)

Documentation

  • Updated llms.txt with command documentation
  • Updated README.md with /firebase command
  • Updated SKILL.md files for doctor and install

Commit Standards

  • Follows Conventional Commits format
  • No breaking changes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added /firebase interactive command and CLI wizard to authenticate with Google, detect and validate Android/iOS Firebase credentials, list projects/apps, and download/install platform config files.
    • Added a Firebase status display with detailed and compact views showing credential validity, issues, and remediation guidance.
    • Integrated a firebase overlay/command into the chat UI.
  • Documentation

    • README and install guides updated with Firebase verification steps and /firebase usage.

✏️ Tip: You can customize this high-level summary in your review settings.

- Detect Firebase credential files (google-services.json, GoogleService-Info.plist)
- Support XML and JSON plist formats for iOS configuration
- Validate credentials against Zod schemas
- Interactive wizard for Firebase configuration management
- OAuth 2.0 with PKCE support for downloading configs from Firebase Console
- Support React Native, Flutter, iOS, and Android platforms

Key features:
- Automatic detection of platform and expected credential file locations
- Comprehensive validation with detailed error messages
- Project ID verification across platforms
- Integration with existing Clix CLI command system

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new /firebase slash command plus an interactive Ink-based Firebase wizard and a complete Firebase feature set: OAuth PKCE auth, Management API client, token storage, credential detection/validation, downloader, Zod schemas/validators, error types, UI, and docs.

Changes

Cohort / File(s) Summary
Docs & Manifests
README.md, llms.txt, package.json
Added /firebase to docs/command lists; bumped command counts; added dependencies google-auth-library, plist, and devDep @types/plist.
Command & Registry
src/lib/commands/firebase.tsx, src/lib/commands/registry.ts
New /firebase Command rendering FirebaseWizard; registered in BUILT_IN_COMMANDS.
Errors
src/lib/errors/types.ts
New Firebase error codes and exported FirebaseError class with platform/file context.
API Client & Types
src/lib/services/firebase/api/*
New FirebaseApiClient, API response types, and api/index.ts barrel for listing projects/apps and fetching config payloads.
Detection & Paths
src/lib/services/firebase/detector.ts
Platform detection, expected-path resolution, discovery/parsing of google-services.json and GoogleService-Info.plist, and detectFirebaseConfig API.
Schemas & Validation
src/lib/services/firebase/schemas.ts, src/lib/services/firebase/validator.ts
Zod full/minimal schemas, typed validators, project ID extraction, and cross-platform validation utilities.
OAuth & Token Storage
src/lib/services/firebase/oauth/*
PKCE GoogleAuthClient, GOOGLE_OAUTH_CONFIG, TokenStore (XDG-backed), OAuth types, and oauth barrel exports.
Downloader Service
src/lib/services/firebase/downloader.ts
FirebaseDownloader handling OAuth flow, project/app listing, app lookup, config download/save, expected save paths, and logout.
Core Service & Barrel
src/lib/services/firebase/firebase-service.ts, src/lib/services/firebase/index.ts
FirebaseService centralizing detection/status/recommendations; index.ts re-exports firebase public API.
Types & Models
src/lib/services/firebase/types.ts
Added platform/credential/issue/status/result types, FIREBASE_HELP_URLS, wizard/action types, and related models.
UI Components
src/ui/components/FirebaseStatusDisplay.tsx, src/ui/components/FirebaseWizard.tsx
New Ink-based FirebaseStatusDisplay and multi-phase FirebaseWizard (auth, project/app selection, download, validate, complete).
Chat Integration & Hooks
src/ui/chat/ChatApp.tsx, src/ui/chat/hooks/useCommandHandler.ts, src/ui/chat/hooks/useOverlays.ts
Added firebase overlay rendering, /firebase slash command handling, overlay type and show/complete/cancel handlers.
Embedded Skills & Docs
src/lib/embedded-skills.ts, src/lib/skills/*/SKILL.md
Updated embedded skill frontmatter and install/doctor docs to include Firebase checks and /firebase command references.
Misc Scripts
scripts/embed-skills.ts
Excluded skill-creator from embedding discovery.

Suggested reviewers

  • JeongwooYoo
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding Firebase configuration detection and validation system with appropriate semantic versioning (feat).
Description check ✅ Passed The PR description is comprehensive and well-structured, following the required template with Summary, Details, How to Validate, and Pre-Merge Checklist sections, all properly completed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0c56059c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Fix all issues with AI agents
In `@llms.txt`:
- Around line 229-237: The documentation added a new system command `/firebase`,
increasing the system commands from 9 to 10, so update the earlier slash-command
totals to read "3 autonomous + 5 skills + 10 system = 18 total" (or update any
"total slash-commands" summary) to avoid inconsistency; locate the earlier
totals/summary text that lists the counts (look for phrases like "autonomous",
"skills", "system", or "total slash-commands") and change the numbers and the
computed total to 18 wherever they appear.

In `@src/lib/services/firebase/detector.ts`:
- Around line 327-382: The detector currently treats any file flagged as
"inExpectedLocation" by findGoogleServicesJson but that helper marks expected
locations globally; update detectAndroidCredential to prefer and label files
using the platform-specific expectedPaths: when selecting expectedFile, find a
file whose path matches any entry in expectedPaths (compare normalized relative
paths or compare absolutePath to path.join(projectPath, expectedPath)), set
inExpectedLocation based on that match, and set expectedPath only when the
chosen file is not inExpectedLocation (use the first matching expectedPaths
value if applicable); apply the same change to the iOS detector
(detectIosCredential / findGoogleServiceInfoPlist) so validation and guidance
use the platform-specific expectedPaths.
- Around line 149-176: The function detectPlatform currently returns
'react-native' when detectNativePlatforms finds both hasIos and hasAndroid even
if isReactNativeProject/isFlutterProject were false; change the logic in
detectPlatform so that when hasIos && hasAndroid and neither
isReactNativeProject(projectPath) nor isFlutterProject(projectPath) are true, it
returns 'unknown' instead of 'react-native' to avoid misclassifying purely
native monorepos; update the hasIos && hasAndroid branch in detectPlatform (and
any related comments) to implement this conditional return and add/update tests
that assert dual-native-but-not-cross-platform repos yield 'unknown'.

In `@src/lib/services/firebase/oauth/auth-client.ts`:
- Around line 96-167: The waitForCallback Promise currently starts a server
bound to all interfaces and never clears the timeout; change server.listen(...)
to bind explicitly to '127.0.0.1' using GOOGLE_OAUTH_CONFIG.callbackPort, store
the setTimeout return value in a variable (e.g., timeoutHandle), and add a small
cleanup helper (e.g., cleanup = () => { clearTimeout(timeoutHandle); try {
server.close(); } catch {} }) that you call before every resolve and reject
(inside the OAuth success branch where resolve({ code, state }) is called, in
the error and missing-code branches before reject, in the server.on('error')
handler, and in the timeout callback) so the server is closed and the timeout
cleared on all code paths.
- Around line 34-89: generate and store a cryptographically-random OAuth state
string when building the URL in generateAuthUrl() (add a private this.state:
string | null), include that state in the client.generateAuthUrl(...)
parameters, and in waitForCallback() validate that the incoming state equals the
stored this.state before accepting the code; if missing or mismatched,
throw/reject and clear the stored state. Ensure state is cleared after
successful or failed validation to avoid reuse.

In `@src/lib/services/firebase/oauth/token-store.ts`:
- Around line 45-49: The save method writes OAuth tokens with default permissive
filesystem bits; change mkdir and file writes to enforce restrictive
permissions: create the directory with mode 0o700 (use fs.mkdir(..., {
recursive: true, mode: 0o700 })) and write the token file with mode 0o600 (use
fs.writeFile with the mode option or follow up with fs.chmod(this.tokenPath,
0o600)). Update the save function (in token-store.ts, method save) to ensure the
directory and file permissions are set atomically/explicitly so the token JSON
is only readable by the owner.

In `@src/lib/services/firebase/schemas.ts`:
- Around line 12-17: The AndroidClientInfoSchema currently allows uppercase
letters due to the case-insensitive regex flag; update the schema's regex in
AndroidClientInfoSchema by removing the `i` flag so the pattern becomes strictly
lowercase (use `/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/`) to enforce lowercase
package names while keeping the existing min(1) and error message intact.

In `@src/ui/components/FirebaseWizard.tsx`:
- Around line 828-833: In handleAction (the async callback handling
CredentialAction), await the async call to handleDownload so the promise is
awaited and potential rejections are propagated/handled; update the 'download'
case to use await handleDownload() (keeping the surrounding async handleAction)
and ensure any needed try/catch around the switch remains or is added to surface
errors from handleDownload.
- Around line 149-162: The openBrowser function is vulnerable to shell injection
and mishandles Windows arguments while ignoring exec errors; replace the
string-interpolated exec call with safe child_process APIs (e.g., spawn or
execFile) and pass the URL as an argument array instead of interpolating into a
shell command, handle Windows by invoking cmd /c start with an explicit empty
title (e.g., ['cmd', ['/c','start','', url]] or equivalent) or call
spawn('start', ['', url], {shell:true}) carefully, and attach an error
callback/listener to surface failures (or log/throw the error) so exec/spawn
errors are not silently ignored; update references in openBrowser and any call
sites accordingly.
🧹 Nitpick comments (13)
src/lib/services/firebase/oauth/types.ts (1)

12-18: Redundant field declarations in extended interface.

OAuthTokens extends Credentials from google-auth-library, which already declares access_token, refresh_token, token_type, and expiry_date. Re-declaring them here is redundant but documents the expected shape explicitly. Consider removing the duplicates or adding a comment explaining the intent.

src/lib/services/firebase/api/types.ts (1)

31-35: Consider adding 'STATE_UNSPECIFIED' to project state union.

The Firebase Management API can return STATE_UNSPECIFIED in addition to ACTIVE and DELETED. Adding it would make the type more robust against unexpected API responses.

Suggested change
   /**
    * Project state.
    */
-  state: 'ACTIVE' | 'DELETED';
+  state: 'STATE_UNSPECIFIED' | 'ACTIVE' | 'DELETED';
src/ui/components/FirebaseStatusDisplay.tsx (3)

99-109: Potential non-unique React key if errors have duplicate path+message.

The key ${error.path}-${error.message} could collide if the same error appears multiple times. Consider appending the index for guaranteed uniqueness.

Suggested fix
-              {credential.errors.slice(0, 3).map((error) => (
-                <Text key={`${error.path}-${error.message}`} color="red">
+              {credential.errors.slice(0, 3).map((error, index) => (
+                <Text key={`${error.path}-${error.message}-${index}`} color="red">

154-166: Consider extracting platform-needs helpers to a shared utility.

platformNeedsAndroid and platformNeedsIos duplicate logic from downloader.ts (lines 179-181). Extract to a shared utility in the firebase module to maintain consistency.


180-187: getStatusColor returns 'red' for both missing and n/a statuses.

Currently n/a and both render as red. Consider using a neutral color (e.g., dimColor or gray) for n/a to differentiate "not applicable" from "missing/error".

Suggested fix
 function getStatusColor(status: string): string {
   if (status === '✓') return 'green';
   if (status === '!') return 'yellow';
+  if (status === 'n/a') return 'gray';
   return 'red';
 }
src/lib/services/firebase/downloader.ts (1)

145-168: Consider adding error handling for file write operations.

downloadAndroidConfig and downloadIosConfig don't catch errors from fs.mkdir or fs.writeFile. If the directory creation or file write fails (e.g., permission denied), the error propagates uncaught. Consider wrapping in try-catch with meaningful error messages.

src/ui/components/FirebaseWizard.tsx (1)

656-706: Consider simplifying callback dependency management.

The ref-based pattern to avoid circular dependencies works but adds complexity. Consider extracting the download flow into a custom hook or using a reducer pattern for cleaner state management.

src/lib/services/firebase/firebase-service.ts (1)

64-71: Consider extracting platform requirements logic to a shared utility.

The needsAndroid/needsIos computation is duplicated across multiple files (here, in FirebaseWizard.tsx, and in detector.ts). Extract this to a shared helper function to ensure consistency.

♻️ Example shared utility
// In types.ts or a new utils.ts
export function getPlatformRequirements(platform: Platform): { needsAndroid: boolean; needsIos: boolean } {
  const needsAndroid = platform === 'android' || platform === 'react-native' || platform === 'flutter';
  const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
  return { needsAndroid, needsIos };
}
src/lib/services/firebase/api/firebase-api.ts (2)

39-56: Consider adding request timeout.

The fetch calls have no timeout configuration. Long-running or hung requests could block the CLI indefinitely. Consider using AbortController with a timeout.

♻️ Example with timeout
 private async request<T>(path: string): Promise<T> {
   const token = await this.getAccessToken();
   const url = `${BASE_URL}${path}`;
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 30000);

-  const response = await fetch(url, {
-    headers: {
-      Authorization: `Bearer ${token}`,
-      'Content-Type': 'application/json',
-    },
-  });
+  try {
+    const response = await fetch(url, {
+      headers: {
+        Authorization: `Bearer ${token}`,
+        'Content-Type': 'application/json',
+      },
+      signal: controller.signal,
+    });

     if (!response.ok) {
       const error = await response.text();
       throw new Error(`Firebase API error (${response.status}): ${error}`);
     }

     return response.json() as Promise<T>;
+  } finally {
+    clearTimeout(timeoutId);
+  }
 }

103-106: Consider URL-encoding path parameters defensively.

While Firebase project IDs and app IDs typically don't contain special characters, encoding them with encodeURIComponent would be a defensive measure against edge cases.

src/lib/services/firebase/types.ts (2)

86-90: Naming conflict with existing ValidationError class.

This ValidationError interface shadows the ValidationError class in src/lib/errors/types.ts. Consider renaming to FirebaseValidationError or CredentialValidationError to avoid confusion when both are imported.


104-125: Consider documenting the valid/content invariant.

The content field is optional, but when valid is true, content should always be defined. This invariant is relied upon in firebase-service.ts. Consider adding a JSDoc comment to document this relationship.

📝 Documentation suggestion
 export interface FirebaseCredentialFile {
   /** File path relative to project root */
   path: string;
   /** Absolute file path */
   absolutePath: string;
   /** Target platform */
   platform: 'android' | 'ios';
   /** Credential file type */
   type: CredentialFileType;
   /** Whether file exists */
   exists: boolean;
   /** Whether file is valid */
   valid: boolean;
   /** Validation errors if any */
   errors: ValidationError[];
-  /** Parsed content if valid */
+  /** Parsed content if valid. Guaranteed to be defined when `valid` is true. */
   content?: GoogleServicesJson | GoogleServiceInfoPlist;
   /** Whether file is in expected location */
   inExpectedLocation: boolean;
   /** Expected location if different from actual */
   expectedPath?: string;
 }
src/lib/services/firebase/detector.ts (1)

607-623: Reuse the existing needsAndroidConfig/needsIosConfig helpers.

Avoids duplicating platform logic and keeps behavior consistent.

♻️ Suggested refactor
-  const needsAndroid =
-    platform === 'android' || platform === 'react-native' || platform === 'flutter';
-  const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
+  const needsAndroid = needsAndroidConfig(platform);
+  const needsIos = needsIosConfig(platform);

- Add CSRF protection via OAuth state parameter validation
- Bind callback server to localhost only for security
- Add proper timeout cleanup in OAuth flow
- Harden token file permissions (0o600 for file, 0o700 for dir)
- Use spawn instead of exec for shell injection prevention
- Add missing await for handleDownload
- Return 'unknown' for dual-platform detection
- Expand iOS plist search to include root-level directories
- Use platform-specific expectedPaths for credential detection
- Enforce lowercase for Android package names in schema
- Update llms.txt slash-command count (17→18)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/lib/services/firebase/detector.ts`:
- Around line 632-648: detectFirebaseConfig currently treats an 'unknown'
platform as not needing Android or iOS, causing configured to be true when both
configs are missing; update detectFirebaseConfig to call
detectNativePlatforms(projectPath) when detectPlatform returns 'unknown' and use
its result to set needsAndroid and needsIos (instead of defaulting to false), so
the boolean flags reflect actual native platform presence before computing
androidConfigured, iosConfigured and configured; reference detectFirebaseConfig,
detectPlatform, detectNativePlatforms, needsAndroid, needsIos and
getExpectedPaths when making the change.
♻️ Duplicate comments (2)
src/lib/services/firebase/oauth/token-store.ts (1)

45-53: Ensure existing token files are re-permissioned too.

fs.writeFile(..., { mode }) only sets permissions on newly created files; if a token file already exists from an older version, it can keep permissive bits. Consider chmod after write (and optionally on the directory) to guarantee owner-only access on upgrades.

🔐 Suggested hardening
   await fs.mkdir(dir, { recursive: true, mode: 0o700 });
   // Write token file with restricted permissions (owner read/write only)
   await fs.writeFile(this.tokenPath, JSON.stringify(tokens, null, 2), {
     encoding: 'utf-8',
     mode: 0o600,
   });
+  // Ensure existing paths are tightened as well
+  await fs.chmod(dir, 0o700);
+  await fs.chmod(this.tokenPath, 0o600);
src/ui/components/FirebaseWizard.tsx (1)

150-169: Handle spawn errors to avoid CLI crashes when browser open fails.

If open/xdg-open/cmd isn’t available, spawn emits an error event; without a handler, Node can throw. Add an error listener (and ideally surface a user-facing message).

🛠 Minimal safeguard
-  spawn(command, args, { detached: true, stdio: 'ignore' }).unref();
+  const child = spawn(command, args, { detached: true, stdio: 'ignore' });
+  child.on('error', (err) => {
+    // eslint-disable-next-line no-console
+    console.error('Failed to open browser:', err.message);
+  });
+  child.unref();

- Merge main branch containing ios-setup feature
- Resolve conflicts in package.json (keep both expo libs and google-auth-library)
- Resolve conflicts in llms.txt (update counts: 19 slash commands = 4 autonomous + 5 interactive + 10 system)
- Regenerate bun.lock with all dependencies

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai coderabbitai bot requested a review from JeongwooYoo January 26, 2026 08:00
pitzcarraldo and others added 4 commits January 26, 2026 17:47
- Add 'firebase' overlay type to useOverlays
- Add showFirebaseWizard, handleFirebaseComplete, handleFirebaseCancel handlers
- Add 'firebase' case to useCommandHandler switch statement
- Render FirebaseWizard component when firebase overlay is active
- Fixes "Command /firebase is not implemented yet" error

Co-Authored-By: Claude <noreply@anthropic.com>
Fixes duplicate 'local-ios-setup' key issue caused by merge.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/lib/embedded-skills.ts`:
- Around line 1555-1870: The EMBEDDED_SKILLS object contains two entries with
the same key 'local-ios-setup', causing the later one to silently overwrite the
earlier; locate the duplicate 'local-ios-setup' entries in EMBEDDED_SKILLS and
either remove the redundant copy or merge their contents into a single
consolidated 'local-ios-setup' value (ensuring no required text is lost), then
run tests/lint to confirm no other duplicate keys remain.
- Around line 647-842: The repo docs are missing the newly added skill
'skill-creator' (clix-skill-creator) declared in embedded-skills.ts; update
README.md and llms.txt to include an entry for "clix-skill-creator" (display
name: Skill Creator, short description: Generate new Clix agent skills) in the
same format/section as other skills, ensuring the trigger phrase
`clix-skill-creator` or `skill-creator` is listed in llms.txt and a brief README
entry mirrors the SKILL.md frontmatter (name/display-name/short-description) so
documentation stays in sync with the embedded-skills.ts addition.
🧹 Nitpick comments (1)
llms.txt (1)

586-617: Document the OAuth download functionality.

The documentation describes detection and validation features well, but it doesn't mention a key capability from the PR objectives: the ability to download missing Firebase configurations from the Firebase Console using OAuth 2.0 with PKCE. The example shows "Press Enter to configure" but doesn't explain what this action does.

📝 Suggested enhancement to document OAuth download feature

Consider adding a bullet point under Features: to mention the download capability:

 **Features:**
 - Auto-detects Firebase credential files in project
 - Validates JSON/plist structure against Firebase schema
 - Reports issues (missing files, invalid format, wrong location)
 - Provides recommendations with help URLs
+- Downloads missing configs from Firebase Console via OAuth 2.0
 - Supports React Native, Flutter, iOS, and Android projects

And clarify the interactive action in the example section:

-Press Enter to configure, Esc to skip
+Press Enter to download missing configs (opens browser for Firebase OAuth), Esc to skip

Based on PR objectives, the OAuth download feature is a significant capability that users should be aware of.

pitzcarraldo and others added 2 commits January 26, 2026 17:55
… true

When platform was 'unknown', the previous logic resulted in:
- needsAndroid = false
- needsIos = false
- androidConfigured = !false = true
- iosConfigured = !false = true
- configured = true (incorrect!)

Now for unknown platform, configured is true only if at least one
valid config file (Android or iOS) is found.

Co-Authored-By: Claude <noreply@anthropic.com>
skill-creator is an internal developer tool for creating new skills,
not intended for end users. Exclude it from the embedded skills to
keep the command list focused on user-facing features.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/lib/services/firebase/detector.ts`:
- Around line 304-335: The Windows path separator causes stored relative plist
paths to use backslashes and mismatch expectedPaths (which use forward slashes);
update both places that compute plistPath in the iOS directory loop and the
root-level native project loop so the relative path is normalized to POSIX style
before any comparisons or pushing into results (e.g., compute plistPath with
path.posix.join or call .replace(/\\+/g, '/')), ensure comparisons against
expectedPaths and the results.some((r) => r.path === plistPath) use the
normalized path, and keep the use of fullPath for fileExists/unix filesystem
checks unchanged.
♻️ Duplicate comments (1)
src/lib/services/firebase/detector.ts (1)

632-655: unknown platform can still mark dual-native as configured with only one config (and suppress issues).

When detectPlatform() returns 'unknown' for dual-native repos, configured becomes true if either config is valid. That contradicts “fully configured” semantics and hides missing-config issues. Consider deriving needsAndroid/needsIos from detectNativePlatforms() when platform is 'unknown', and use those flags for both issue generation and configured.

🛠️ Proposed fix
 export async function detectFirebaseConfig(projectPath: string): Promise<FirebaseDetectionResult> {
   const platform = await detectPlatform(projectPath);
+  const native =
+    platform === 'unknown' ? await detectNativePlatforms(projectPath) : { hasIos: false, hasAndroid: false };
   const expectedPaths = getExpectedPaths(platform, projectPath);

   const android = await detectAndroidCredential(projectPath, expectedPaths.android);
   const ios = await detectIosCredential(projectPath, expectedPaths.ios);

-  const issues = generateIssues(android, ios, platform, expectedPaths);
+  const needsAndroid =
+    needsAndroidConfig(platform) || (platform === 'unknown' && native.hasAndroid);
+  const needsIos =
+    needsIosConfig(platform) || (platform === 'unknown' && native.hasIos);
+  const issues =
+    platform === 'unknown'
+      ? [
+          ...(needsAndroid ? generateAndroidIssues(android, expectedPaths.android[0]) : []),
+          ...(needsIos ? generateIosIssues(ios, expectedPaths.ios[0]) : []),
+        ]
+      : generateIssues(android, ios, platform, expectedPaths);

   // Determine if Firebase is configured
-  const needsAndroid =
-    platform === 'android' || platform === 'react-native' || platform === 'flutter';
-  const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
-
   let configured: boolean;
   if (platform === 'unknown') {
-    // For unknown platform, configured is true only if at least one valid config exists
-    configured = (android?.valid ?? false) || (ios?.valid ?? false);
+    const androidConfigured = !needsAndroid || (android?.valid ?? false);
+    const iosConfigured = !needsIos || (ios?.valid ?? false);
+    configured = androidConfigured && iosConfigured;
   } else {
     const androidConfigured = !needsAndroid || (android?.valid ?? false);
     const iosConfigured = !needsIos || (ios?.valid ?? false);
     configured = androidConfigured && iosConfigured;
   }

Use POSIX path separators for relative plist paths stored in results
array to ensure consistent forward slashes across platforms. This
fixes false "misplaced" warnings on Windows where path.join produces
backslashes that don't match expectedPaths.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/lib/services/firebase/detector.ts`:
- Around line 67-75: The isReactNativeProject function can throw or produce
false negatives because it spreads pkg.dependencies and pkg.devDependencies
which may be undefined; update the merge to guard with defaults (e.g., use {}
fallback for pkg.dependencies and pkg.devDependencies or use optional chaining)
so deps is always an object, then check deps['react-native'] || deps.expo as
before; ensure you update the code paths in isReactNativeProject to handle
missing package.json fields without throwing.
♻️ Duplicate comments (1)
src/lib/services/firebase/detector.ts (1)

634-667: unknown platform can mask missing configs in dual-native repos.

When detectPlatform returns unknown for dual-native projects, configured becomes true if either config is valid and issues stay empty (because needsAndroidConfig/needsIosConfig ignore unknown). This can hide a missing iOS or Android config. Consider using detectNativePlatforms to determine which platforms exist and generate issues/configured accordingly.

🛠️ Suggested fix (keeps current behavior for truly unknown projects)
 export async function detectFirebaseConfig(projectPath: string): Promise<FirebaseDetectionResult> {
   const platform = await detectPlatform(projectPath);
   const expectedPaths = getExpectedPaths(platform, projectPath);

   const android = await detectAndroidCredential(projectPath, expectedPaths.android);
   const ios = await detectIosCredential(projectPath, expectedPaths.ios);

-  const issues = generateIssues(android, ios, platform, expectedPaths);
+  let issues = generateIssues(android, ios, platform, expectedPaths);

   // Determine if Firebase is configured
-  // For unknown platform, check if at least one valid config file exists
-  const needsAndroid =
-    platform === 'android' || platform === 'react-native' || platform === 'flutter';
-  const needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';
+  let needsAndroid =
+    platform === 'android' || platform === 'react-native' || platform === 'flutter';
+  let needsIos = platform === 'ios' || platform === 'react-native' || platform === 'flutter';

   let configured: boolean;
   if (platform === 'unknown') {
-    // For unknown platform, configured is true only if at least one valid config exists
-    configured = (android?.valid ?? false) || (ios?.valid ?? false);
+    const native = await detectNativePlatforms(projectPath);
+    if (native.hasAndroid || native.hasIos) {
+      needsAndroid = native.hasAndroid;
+      needsIos = native.hasIos;
+      issues = [];
+      if (needsAndroid) {
+        issues.push(...generateAndroidIssues(android, expectedPaths.android[0]));
+      }
+      if (needsIos) {
+        issues.push(...generateIosIssues(ios, expectedPaths.ios[0]));
+      }
+      const androidConfigured = !needsAndroid || (android?.valid ?? false);
+      const iosConfigured = !needsIos || (ios?.valid ?? false);
+      configured = androidConfigured && iosConfigured;
+    } else {
+      configured = (android?.valid ?? false) || (ios?.valid ?? false);
+    }
   } else {
     const androidConfigured = !needsAndroid || (android?.valid ?? false);
     const iosConfigured = !needsIos || (ios?.valid ?? false);
     configured = androidConfigured && iosConfigured;
   }
🧹 Nitpick comments (1)
src/lib/services/firebase/detector.ts (1)

184-210: Expand iOS expected paths for RN/native layouts.

For react-native and ios, expectedPaths.ios only includes ios/GoogleService-Info.plist or root. Common layouts place the plist under ios/<App>/ or <App>/ (sibling to .xcodeproj), which will be flagged as “misplaced” and produce misleading recommendations. Consider deriving iOS expected paths from detected app directories (e.g., scan for .xcodeproj/.xcworkspace siblings or reuse the app-specific plist discovery results) and include those paths in expectedPaths.ios.

Comment on lines +67 to +75
async function isReactNativeProject(projectPath: string): Promise<boolean> {
try {
const packageJson = await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8');
const pkg = JSON.parse(packageJson);
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
return Boolean(deps['react-native'] || deps.expo);
} catch {
return false;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against undefined dependency maps.

Object spread on pkg.dependencies/pkg.devDependencies can throw if either is undefined, which turns RN detection into a false negative.

🛠️ Proposed fix
-    const deps = { ...pkg.dependencies, ...pkg.devDependencies };
+    const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function isReactNativeProject(projectPath: string): Promise<boolean> {
try {
const packageJson = await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8');
const pkg = JSON.parse(packageJson);
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
return Boolean(deps['react-native'] || deps.expo);
} catch {
return false;
}
async function isReactNativeProject(projectPath: string): Promise<boolean> {
try {
const packageJson = await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8');
const pkg = JSON.parse(packageJson);
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
return Boolean(deps['react-native'] || deps.expo);
} catch {
return false;
}
🤖 Prompt for AI Agents
In `@src/lib/services/firebase/detector.ts` around lines 67 - 75, The
isReactNativeProject function can throw or produce false negatives because it
spreads pkg.dependencies and pkg.devDependencies which may be undefined; update
the merge to guard with defaults (e.g., use {} fallback for pkg.dependencies and
pkg.devDependencies or use optional chaining) so deps is always an object, then
check deps['react-native'] || deps.expo as before; ensure you update the code
paths in isReactNativeProject to handle missing package.json fields without
throwing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants